package com.zl.shirodemo.config;

import java.util.List;

import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.authz.Authorizer;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.spring.config.web.autoconfigure.ShiroWebAutoConfiguration;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.filter.InvalidRequestFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ShiroConfig {
    
    /**
     * 配置一些不需要登录就能访问的url，例如身份验证url
     */
    @Value("${shiro.noNeedLoginURLs}")
    private List<String> noNeedLoginURLs;
    
    @Value("${shiro.password-hashIterations}")
    private int passwordHashIterations;
    
    /**
     * 关闭shiro的session功能
     * @see ShiroWebAutoConfiguration#sessionStorageEvaluator()
     * shiro-spring-boot-web-starter 里面配置的SessionStorageEvaluator 是自动开启了session功能的
     * 作为前后端分离项目，在这里覆盖自动配置的Bean,关闭session功能，节省下服务器的宝贵空间
     */
    @Bean
    public SessionStorageEvaluator sessionStorageEvaluator() {
        DefaultWebSessionStorageEvaluator defaultWebSessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
        defaultWebSessionStorageEvaluator.setSessionStorageEnabled(false);
        return defaultWebSessionStorageEvaluator;
    }
    
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
        if (noNeedLoginURLs != null) {
            noNeedLoginURLs.forEach(url -> {
                chainDefinition.addPathDefinition(url, DefaultFilter.anon.name());
    
            });
        }
    
        /**
         * 此处是shiro支持token的关键，前端需要在请求的header中设置Bearer Token, 此拦截器会获取token，并自动登录
         * 关于Bearer验证方式可以自行百度
         */
        chainDefinition.addPathDefinition("/**", DefaultFilter.authcBearer.name());
        return chainDefinition;
    }
    
    /**
     * 解决url带有中文报非法请求的问题
     *
     * @return
     */
    @Bean
    public InvalidRequestFilter invalidRequest() {
        InvalidRequestFilter invalidRequestFilter = new InvalidRequestFilter();
        invalidRequestFilter.setBlockNonAscii(false);
        return invalidRequestFilter;
    }
    
    /**
     * 解决我们自定义了Realm后无法启动的问题
     * https://blog.csdn.net/qq_34741165/article/details/83720666
     * @return
     */
    @Bean
    public Authorizer authorizer() {
        ModularRealmAuthorizer authorizer = new ModularRealmAuthorizer();
        return authorizer;
    }
    
    @Bean
    public RedisCacheManager cacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setExpire(200000);
        return redisCacheManager;
    }
    
    @Bean
    public RedisManager redisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("127.0.0.1:6379");
        return redisManager;
    }
    
    
    /**
     * 比较传入的密码和数据库的密码是否一致
     * 这里定义了密码加密的一些内容
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(Md5Hash.ALGORITHM_NAME);
        hashedCredentialsMatcher.setHashIterations(passwordHashIterations);
        return hashedCredentialsMatcher;
    }
    
    
    @Bean
    public Authenticator authenticator() {
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setAuthenticationStrategy(new SupportSuccessfulStrategy());
        return authenticator;
    }
    
}
